/** \file    refine/topology_limit.cpp
  * \brief   Calculates possible limits on mask's value that will not change topology of objects.
  * \date    2003, 2004
  * \author  Fedor Chelnokov
  * \version $Header: /home/cscom1r4/cvsroot/cs/refine/topology_limit.cpp,v 1.3 2005/09/10 19:09:41 cscom1r4 Exp $
  */

#include "refine/topology_limit.h"
#include "imglib/bitmap.h"
#include "imglib/imageclass.h"
#include <algorithm>
#include <assert.h>
#include <xtd/minmax>

namespace refine {

static const int_point nei_points[8] = {
	int_point(-1,  0),
	int_point(-1, +1),
	int_point( 0, +1),
	int_point(+1, +1),
	int_point(+1,  0),
	int_point(+1, -1),
	int_point( 0, -1),
	int_point(-1, -1)
};

namespace bit_mask {

static const vsituation vs_cases[256] = {
	 fo, fo, fo, fo, fo, fo, fo, fl, fo,pbt,pbt,pbt, fo,pbt, fl,any,
	 fo, fo,pbt,pbt, fo, fo,pbt,pbt, fo,pbt,pbt,pbt, fl,pbt,any, tl,
	 fo,pbt,pbt,pbt,pbt,pbt,pbt,pbt,pbt,pbt,pbt,pbt,pbt,pbt,pbt,pbt,
	 fo,pbt,pbt,pbt,pbt,pbt,pbt,pbt, fl,pbt,pbt,pbt,any,pbt, tl, to,
	 fo, fo,pbt,pbt, fo, fo,pbt,pbt,pbt,pbt,pbt,pbt,pbt,pbt,pbt,pbt,
	 fo, fo,pbt,pbt, fo,pbt,pbt,pbt,pbt,pbt,pbt,pbt,pbt,pbt,pbt,pbt,
	 fo,pbt,pbt,pbt,pbt,pbt,pbt,pbt,pbt,pbt,pbt,pbt,pbt,pbt,pbt,pbt,
	 fl,pbt,pbt,pbt,pbt,pbt,pbt,pbt,any,pbt,pbt,pbt, tl,pbt, to, to,
	 fo, fo,pbt, fl,pbt,pbt,pbt,any,pbt,pbt,pbt,pbt,pbt,pbt,pbt, tl,
	pbt,pbt,pbt,pbt,pbt,pbt,pbt,pbt,pbt,pbt,pbt,pbt,pbt,pbt,pbt, to,
	pbt,pbt,pbt,pbt,pbt,pbt,pbt,pbt,pbt,pbt,pbt, to,pbt,pbt, to, to,
	pbt,pbt,pbt,pbt,pbt,pbt,pbt,pbt,pbt,pbt, to, to,pbt,pbt, to, to,
	 fo, fl,pbt,any,pbt,pbt,pbt, tl,pbt,pbt,pbt,pbt,pbt,pbt,pbt, to,
	pbt,pbt,pbt,pbt,pbt,pbt,pbt,pbt,pbt,pbt,pbt,pbt,pbt,pbt,pbt, to,
	 fl,any,pbt, tl,pbt,pbt,pbt, to,pbt,pbt, to, to,pbt,pbt, to, to,
	any, tl,pbt, to,pbt,pbt,pbt, to, tl, to, to, to, to, to, to, to};

vsituation topology_limit(const mask & m, int_point p, bool minor)
{
	int vicinity_code = 0;
	for (int i = 0; i < 8; ++i)
		vicinity_code = (vicinity_code << 1) + (m[p + nei_points[i]] ? 1 : 0);
	if (minor) {
		if (vicinity_code == 255)
			return to;
		else if (vicinity_code == 0)
			return fo;
		else
			return any;
	}
	return vs_cases[vicinity_code];
}

} //namespace bit_mask

namespace grey_mask {

static void topology_limit_impl(const mask_value u[8], mask_value & min, mask_value & max)
{
	mask_value v[8];
	std::copy(u, u+8, v);
	std::sort(v, v+8);
	min = v[0];
	max = v[7];
	mask_value * vend = std::unique(v, v+8);

	for (mask_value * iv = v; iv+1 != vend; ++iv) {
		mask_value t0 = iv[0];
		mask_value t1 = iv[1];
		int vicinity_code = 0;
		for (int i = 0; i < 8; ++i)
			vicinity_code = (vicinity_code << 1) + ii(u[i] > t0);
		using namespace bit_mask;
		switch (vs_cases[vicinity_code]) {
		case pbt:
			min = 1; max = 0; return;
		case any:
			break;
		case fo:
			xtd::min(max, t0);
			break;
		case to:
			assert(min <= t1);
			min = t1;
			if (min > max) return;
			break;
		case fl:
			xtd::min(max, t1);
			break;
		case tl:
			assert(min <= t0);
			min = t0;
			if (min > max) return;
			break;
		/*case fll:
			xtd::min(max, (t0+t1)/2);
			break;
		case tll:
			assert(min <= (t0+t1)/2);
			min = (t0+t1)/2;
			if (min > max) return;
			break;*/
		default:
			assert(false);
		}
	}
}

template <class M>
void topology_limit(const M & m, int_point p, mask_value & min, mask_value & max)
{
	mask_value u[8];
	for (int i = 0; i < 8; ++i)
		u[i] = m[p + nei_points[i]];

	return topology_limit_impl(u, min, max);
}

template 
void topology_limit(const mask & m, int_point p, mask_value & min, mask_value & max);

template 
void topology_limit(const img::projection_raster<byte_image, mask_value> & m, int_point p, mask_value & min, mask_value & max);

template 
void topology_limit(const img::projection_raster<argb_byte_image, mask_value> & m, int_point p, mask_value & min, mask_value & max);

} //namespace grey_mask

} //namespace refine
